package com.xiaohong.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.xiaohong.goods.feign.SkuFeign;
import com.xiaohong.goods.pojo.Sku;
import com.xiaohong.search.pojo.SkuInfo;
import com.xiaohong.search.service.SearchSkuService;
import entity.Result;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 参考文档  https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.7/_index_apis.html
 */

@Service
//@SpringBootTest
public class SearchSkuServiceImpl implements SearchSkuService {

    private static final int shards = 1;  //分配的主分片的数量

    private static final int replicas = 0;  //为每一个主分片分配的副本分片的数量

    //索引库名称  注意要小写
    private static final String INDICE = "xiaohong_sku";

    @Autowired
    private SkuFeign skuFeign;


    //这样写操作完成后无法关闭 强制关闭后又无法重启
    /*@Qualifier("getRestHighLevelClient")  //根据名称注入bean
    @Autowired   //ES高版本操作对象
    private RestHighLevelClient client;*/

    //创建索引请求
    CreateIndexRequest createIndexRequest = new CreateIndexRequest(INDICE);

    //获取索引请求
    GetIndexRequest getIndexRequest = new GetIndexRequest(INDICE);

    //删除索引请求
    DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(INDICE);

    // 如果为空参则会搜索所有的索引库
    //SearchRequest searchRequest = new SearchRequest();
    // 指定搜索索引库 建立搜索请求
    SearchRequest searchRequest = new SearchRequest(INDICE);

    //构建搜索对象 的构建对象
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    //搜索条件构建对象
    private QueryBuilder queryBuilder;

    /**
     * 创建连接
     * @return
     */
    public RestHighLevelClient createClient(){
        return new RestHighLevelClient(
                //如果是集群再配置多个
                RestClient.builder(new HttpHost("47.102.218.218",9200,"http"))
        );
    }

    /**
     * 重新导入数据
     * @throws IOException
     */
    @Override
    public void reImportData() throws IOException {
        // 先删除再导入
        if (isIndexExists()){
            deleteIndex();
        }
        importData();
    }

    /**
     * 导入数据到索引库中
     */
    @Override
    public void importData() throws IOException {
        //创建client
        RestHighLevelClient client = createClient();

        try {
            //判断index索引是否操作 注意索引小写
            if (isIndexExists()){
                //如果索引存在  执行更新操作
                System.out.println("索引已存在，执行更新操作！");
            }else {
                //如果索引不存在 执行增加操作

                //创建索引
                createIndex();

                //获取已经上架sku集合
                //Result<List<Sku>> listResult = skuFeign.findByStatus("1");
                //查询全部状态正常Sku数据
                List<Sku> skuList = skuFeign.findAllMarket().getData();

                //转为SkuInfo类型
                List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(skuList), SkuInfo.class);


                //将查询到的spec数据转换为Map
                for (int i = 0; i < skuList.size(); i++) {
                    String spec = skuInfos.get(i).getSpec();
                    Map map = JSON.parseObject(spec, Map.class);
                    skuInfos.get(i).setSpecMap(map);
                    // 时间格式转换
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    skuInfos.get(i).setGmtCreate(simpleDateFormat.format(skuList.get(i).getGmtCreate()));
                    skuInfos.get(i).setGmtModified(simpleDateFormat.format(skuList.get(i).getGmtModified()));
                }
               /* for (SkuInfo skuInfo : skuInfos) {
                    String spec = skuInfo.getSpec();
                    Map map = JSON.parseObject(spec, Map.class);
                    skuInfo.setSpecMap(map);
                    // 时间格式转换
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String create = simpleDateFormat.format(skuInfo.getGmtCreate());
                    String modified = simpleDateFormat.format(skuInfo.getGmtModified());
                    skuInfo.setGmtCreate(create);
                    skuInfo.setGmtModified(modified);
                }*/


                /*IndexRequest indexRequest =  null;
                Map<String,Object> map = null;
                //批量插入
                for(SkuInfo skuInfo:skuInfos){
                    //存入时要将skuInfo转为map格式
                    map = JSON.parseObject(JSON.toJSONString(skuInfo), new TypeReference<Map<String, Object>>() {});
                    //创建索引库连接 并保存数据
                    indexRequest = new IndexRequest(INDICE)
                            .id(skuInfo.getId()+"").source(map);

                    //查看返回结果
                    *//*try {
                        IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
                        System.out.println("Document创建成功");
                    } catch(ElasticsearchException e) {
                        if (e.status() == RestStatus.CONFLICT) {
                            System.out.println("版本冲突错误");
                        }*//*
                }*/

                //创建批量请求
                BulkRequest bulkRequest = new BulkRequest();
                //超时时间
                bulkRequest.timeout(TimeValue.timeValueMinutes(2));

                //考虑到机器性能问题，这里控制每10000条执行一次
                int i  = 0;
                //批量插入
                for(SkuInfo skuInfo:skuInfos){
                    i++;
                    bulkRequest.add(
                            new IndexRequest(INDICE)
                                    .id(""+skuInfo.getId())
                                    .source(JSON.toJSONString(skuInfo), XContentType.JSON)
                    );
                    if (i == 10000){
                        //发送请求
                        BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
                        //获取是否失败标志
                        if (!bulk.hasFailures()){
                            System.out.println("导入10000条数据成功！");
                            //创建新的批量请求
                            bulkRequest = new BulkRequest();
                            //超时时间
                            bulkRequest.timeout(TimeValue.timeValueMinutes(2));
                            i = 0;
                        }else {
                            // 删除索引
                            this.deleteIndex();
                            throw new RuntimeException("导入10000条数据失败！");
                        }
                    }
                }

                //最后把不足10000条的导入
                //发送请求
                BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
                //获取是否失败标志
                if (!bulk.hasFailures()){
                    System.out.println("导入剩下的数据成功！");
                    //释放资源(不然时间久了会出现远程强制关闭了一个连接的情况)
                    client.close();
                }else {
                    // 删除索引
                    this.deleteIndex();
                    throw new RuntimeException("导入剩下的数据失败！");
                }
            }

        }catch (Exception e) {
            e.printStackTrace();
            // 删除索引
            if (isIndexExists()){
                this.deleteIndex();
            }
            throw new RuntimeException("添加数据失败");
        }

    }


    /**
     * 判断索引是否存在
     * @return
     */
    public Boolean isIndexExists() throws IOException {
        RestHighLevelClient client = null;
        try {
            //创建client
            client = createClient();

            Boolean flag = false;
            flag = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
            return flag;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("判断失败");
        }finally {
            //释放资源(不然时间久了会出现远程强制关闭了一个连接的情况)
            client.close();
        }

    }

    /**
     * 删除索引
     */
    public void deleteIndex() throws IOException {
        RestHighLevelClient client = null;
        try {
            //创建client
            client = createClient();

            //等待所有节点将索引删除确认为超时的超时时间
            deleteIndexRequest.timeout(TimeValue.timeValueMinutes(2));

            //连接到主节点的超时时间
            deleteIndexRequest.masterNodeTimeout(TimeValue.timeValueMinutes(1));

            //删除索引
            client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("删除索引失败");
        } finally {
            //释放资源(不然时间久了会出现远程强制关闭了一个连接的情况)
            client.close();
        }
    }

    /**
     * 创建索引
     */
    public void createIndex() throws IOException {
        RestHighLevelClient client = null;
        try {
            //创建client
            client = createClient();

            //创建mapping
            createMapping();

            // 创建索引时，分配的主分片的数量
            createIndexRequest.settings(Settings.builder().put("index.number_of_shards",shards).
                    // 创建索引时，为每一个主分片分配的副本分片的数量
                            put("index.number_of_replicas",replicas));

            //设置等待所有节点将索引创建确认为超时的时间 2分钟
            createIndexRequest.setTimeout(TimeValue.timeValueMinutes(2));

            //连接到主节点的超时时间
            createIndexRequest.setMasterTimeout(TimeValue.timeValueMinutes(2));

            //创建索引
            client.indices().create(createIndexRequest, RequestOptions.DEFAULT);

            System.out.println("创建索引成功！");
            //返回的值CreateIndexResponse允许检索有关已执行操作的信息
            //boolean acknowledged = createIndexResponse.isAcknowledged();
            //boolean shardsAcknowledged = createIndexResponse.isShardsAcknowledged();
        } catch (IOException e) {
            System.out.println("创建索引失败！");
        } finally {
            client.close();
        }

    }

    /**
     * 创建映射
     * @return
     */
    protected void createMapping() {
        /**
         * 常用type类型（其他的看官方文档 https://www.elastic.co/guide/en/elasticsearch/reference/7.7/mapping.html）：
         * keyword 用于索引结构化内容（例如ID，电子邮件地址，主机名，状态代码，邮政编码或标签）的字段
         *         它们通常用于过滤（找到我的所有博客文章，其中 status为published），排序，和聚合。关键字字段只能按其确切值进行搜索，也就意味着不能分词
         * text： 用于索引全文值的字段 一般用于需要分词的数据 文本字段不用于排序，很少用于聚合
         *        有时，在同一字段中同时具有全文本（text）和关键字（keyword）版本会很有用：
         *        一种用于全文本搜索，另一种用于聚合和排序。这可以通过设置多type类型来实现 使用方法参考：https://www.elastic.co/guide/en/elasticsearch/reference/7.7/multi-fields.html
         * date 日期数据类型 日期格式可以自定义，但是如果未format指定，则使用默认格式 “ strict_date_optional_time || epoch_millis”  以毫秒存储
         * format 和日期类型搭配使用，设置日期类型
         *        通过||使用分隔符将它们分开，可以指定多种格式。依次尝试每种格式，直到找到匹配的格式
         *        如： "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
         * long 数值数据类型 支持范围从-2^63到2^63-1.
         * integer 数值数据类型 支持范围从-2^31到2^31-1.
         * double 双精度浮点型
         * boolean  True values：true, "true"    False values: false, "false", "" (empty string)
         * ip 存储 IPv4 or IPv6字段 如："192.168.1.1" "192.168.0.0/16"
         *
         * 其他映射字段
         * analyzer  为字段指定分析器(创建索引的分词器)  如果提供，它将覆盖其他所有搜索分析器
         * search_analyzer  指定搜索分析器
         *
         * 这里使用的时ik中文分词器 （ES内提供了分词器，但是是针对英文的）
         * 1、ik_max_word
         * 会将文本做最细粒度的拆分，比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
         * 2、ik_smart
         * 会做最粗粒度的拆分，比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
         */

        //创建mappings properties：属性
        Map<String,Object> name = new HashMap<>();  //sku名称
        name.put("type","text");
        name.put("analyzer","ik_max_word");
        name.put("search_analyzer","ik_max_word");
        Map<String,Object> price = new HashMap<>();  //价格 单位为：分
        price.put("type","long");
        Map<String,Object> num = new HashMap<>(); // 库存数量
        num.put("type","integer");
        Map<String,Object> gmtCreate = new HashMap<>(); //创建时间
        gmtCreate.put("type","date");
        gmtCreate.put("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis");
        Map<String,Object> gmtModified = new HashMap<>(); //更新时间
        gmtModified.put("type","date");
        gmtModified.put("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis");
        Map<String,Object> spuId = new HashMap<>();  //spu的id
        spuId.put("type","keyword");
        Map<String,Object> categoryId = new HashMap<>();  //类目id
        categoryId.put("type","keyword");
        Map<String,Object> categoryName = new HashMap<>();  //类目名称
        categoryName.put("type","keyword");
        Map<String,Object> brandId = new HashMap<>();  //品牌ID
        brandId.put("type","keyword");
        Map<String,Object> brandChineseName = new HashMap<>(); // 品牌中文名称
        brandChineseName.put("type","keyword");
        Map<String,Object> brandEnglishName = new HashMap<>(); // 品牌英文名称
        brandEnglishName.put("type","keyword");
        Map<String,Object> spec = new HashMap<>();  //规格
        spec.put("type","keyword");
        Map<String,Object> saleNum = new HashMap<>();  //销量
        saleNum.put("type","long");
        Map<String,Object> commentNum = new HashMap<>();  //评论数
        commentNum.put("type","long");
        Map<String,Object> status = new HashMap<>();  //商品状态
        status.put("type","keyword");
        Map<String,Object> images = new HashMap<>();  //商品图片集合
        images.put("type","keyword");
        Map<String,Object> properties = new HashMap<>(); // properties集合
        properties.put("name",name);
        properties.put("price",price);
        properties.put("num",num);
        properties.put("gmtCreate",gmtCreate);
        properties.put("gmtModified",gmtModified);
        properties.put("spuId",spuId);
        properties.put("categoryId",categoryId);
        properties.put("categoryName",categoryName);
        properties.put("brandId",brandId);
        properties.put("brandChineseName",brandChineseName);
        properties.put("brandEnglishName",brandEnglishName);
        properties.put("saleNum",saleNum);
        properties.put("commentNum",commentNum);
        properties.put("images",images);
        Map<String, Object> mapping = new HashMap<>();
        mapping.put("properties", properties);
        //提供Map类型映射源，Map它会自动转换为JSON格式
        try {
            createIndexRequest.mapping(mapping);
            System.out.println("创建mapping成功");
        } catch (Exception e) {
            System.out.println("创建mapping失败");
        }
    }


    //文档的增删改查

    /**
     * 添加单条文档记录
     * @param skuInfo
     * @throws IOException
     * @return
     */
    public IndexResponse addOneDocument(SkuInfo skuInfo) throws IOException {
        RestHighLevelClient client = null;
        IndexResponse indexResponse = null;
        try {
            //创建client
            client = createClient();
            //创建索引对象请求
            IndexRequest request = new IndexRequest(INDICE);
            request.id(skuInfo.getId()+"");//设置id
            //request.timeout("1s");//设置超时时间
            //利用fastJSON将对象转换为json格式，然后放到索引中，参数XContentType.JSON是告诉es为json数据
            request.source(JSON.toJSONString(skuInfo), XContentType.JSON);
            //发送请求
            indexResponse = client.index(request, RequestOptions.DEFAULT);
            //System.out.println(indexResponse.status());//CREATED，说明添加成功

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            client.close();
        }

        return indexResponse;
    }

    /**
     * 获取单条文档
     * @param id
     * @return
     * @throws IOException
     */
    public String getDocument(String id) throws IOException {
        RestHighLevelClient client = null;
        String document = null;
        try {
            //创建client
            client = createClient();
            //注意，文档操作是new GetRequest，而不是getIndexRequest（索引操作）
            GetRequest getRequest = new GetRequest(INDICE, id);
            boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
            //判断此id下的文档记录是否存在
            if (exists){
                //存在的话尝试获取相关信息
                GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
                document = getResponse.getSourceAsString();//获取到所有信息
                //System.out.println(getResponse.getSourceAsString());//将source内容以字符串的形式打印出来
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            client.close();
        }

        return document;
    }

    /**
     * 更新文档记录
     * @param id
     * @param skuInfo
     * @return
     * @throws IOException
     */
    public UpdateResponse updateDocument(String id, SkuInfo skuInfo) throws IOException {
        RestHighLevelClient client = null;
        UpdateResponse updateResponse = null;
        try {
            //创建client
            client = createClient();
            UpdateRequest updateRequest = new UpdateRequest(INDICE, id);
            //规则
            updateRequest.timeout("1s");
            //需要更新的数据
            updateRequest.doc(JSON.toJSONString(skuInfo), XContentType.JSON);
            //客户端执行请求
            updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);

            //System.out.println(updateResponse.status());//ok，表示更新成功
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            client.close();
        }
        return updateResponse;
    }

    /**
     * 删除文档记录
     * @param id
     * @return
     * @throws IOException
     */
    public DeleteResponse deleteDocument(String id) throws IOException {
        RestHighLevelClient client = null;
        DeleteResponse deleteResponse = null;
        try {
            //创建client
            client = createClient();
            DeleteRequest deleteRequest = new DeleteRequest(INDICE, id);
            //deleteRequest.timeout("1s");
            deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);

            //System.out.println(deleteResponse.status());//OK，表明删除成功
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            client.close();
        }
        return deleteResponse;
    }

/*    //还可以批量增删改查
    void doBulk() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        //设置超时时间，如果数据量比较大，时间可以给多一点
        bulkRequest.timeout("3s");
        //创建一个数组存放数据
        List<User> userList = new ArrayList<>();
        userList.add(new User("java", 1));
        userList.add(new User("php", 1));
        userList.add(new User("go", 1));
        userList.add(new User("java2", 1));
        userList.add(new User("php1", 1));
        userList.add(new User("go2", 1));
        //for循环操作
        for (int i = 0; i < userList.size(); i++) {
            bulkRequest.add(new IndexRequest("ryan_index")
                    .id("" + (i + 1))
                    .source(JSON.toJSONString(userList.get(i)), XContentType.JSON)
            );
        }
        //客户端执行请求
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(bulkResponse);
        System.out.println(bulkResponse.status());//ok,表明添加成功
    }*/



    /**
     * 多条件搜索（代码优化后最终结果）
     * @param searchMap
     * @return
     */
    @Override
    public Map<String,Object> search(Map<String, String> searchMap) throws IOException {
        RestHighLevelClient client = null;
        //初始化返回结果
        Map resultMap = null;
        try {
            //创建client
            client = createClient();

            //初始化资源
            searchRequest = new SearchRequest(INDICE);
            searchSourceBuilder = new SearchSourceBuilder();

            //1.获取关键字的值
            String keywords = searchMap.get("keywords");

            if (StringUtils.isEmpty(keywords)) {
                keywords = "华为";//赋值给一个默认的值
            }
            //2.创建查询对象 的构建对象
            //NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

            //3.设置查询的条件

            //设置分组条件  商品分类
            searchSourceBuilder.aggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));

            //设置分组条件  商品品牌
            searchSourceBuilder.aggregation(AggregationBuilders.terms("skuBrandgroup").field("brandChineseName").size(50));

            //设置分组条件  商品的规格
            searchSourceBuilder.aggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(10000));



            //设置高亮条件
        /*searchSourceBuilder.highlighter(new HighlightBuilder().field(new HighlightBuilder.Field("name")));
        searchSourceBuilder.highlighter(new HighlightBuilder().preTags("<em style=\"color:red\">").postTags("</em>"));*/
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("name");//想要的高亮字段
            highlightBuilder.preTags("<span style='color:red'>");//前缀
            highlightBuilder.postTags("</span>");//后缀
            searchSourceBuilder.highlighter(highlightBuilder);

            //设置主关键字查询
            searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keywords,"name","brandChineseName","categoryName"));


            //多条件查询构建条件参数
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();


            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("brandChineseName", searchMap.get("brand")));
            }

            if (!StringUtils.isEmpty(searchMap.get("status"))) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("status", searchMap.get("status")));
            }

            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", searchMap.get("category")));
            }

            //规格过滤查询
            if (searchMap != null) {
                for (String key : searchMap.keySet()) {
                    if (key.startsWith("spec_")) {
                        //特殊字符处理
                        String value = searchMap.get (key).replace( "//","");
                        boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", value));
                    }
                }
            }

            String price = searchMap.get("price");
            if (!StringUtils.isEmpty(price)){
                //price 0-500元 500-1000元 1000-1500元  1500-2000元 2000-3000元 3000元以上
                //去掉中文元和以上0-500 500- 1000 1000-1500 1500-2000 2000-3000 3000
                price = price.replace("元", "").replace("以上", "");
                //prices[]根据-分割 [0, 500] [500, 1000] ... [3000]
                String[] prices = price.split("-");
                if (prices!=null&&prices.length>0){
                    //x一定不为空，y有可能为null
                    //prices[O]!=null price>prices[0] price<=prices[1]
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(prices[0])));
                    if (prices.length==2){
                        boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[1])));
                    }
                }
            }


            //构建过滤查询
            searchSourceBuilder.postFilter(boolQueryBuilder);

            //构建分页查询
            Integer pageNum = 1;
            if (!StringUtils.isEmpty(searchMap.get("pageNum"))) {
                try {
                    pageNum = Integer.valueOf(searchMap.get("pageNum"));
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                    pageNum=1;
                }
            }
            Integer pageSize = 10;
            searchSourceBuilder.from(pageNum-1); // 搜索结果下标是从0开始的，这里一定要减1
            searchSourceBuilder.size(pageSize);


            //构建排序查询
            String sortRule = searchMap.get("sortRule");
            String sortField = searchMap.get("sortField");
            if (!StringUtils.isEmpty(sortRule) && !StringUtils.isEmpty(sortField)) {
                //这里就不用valueOf(sortRule)了，容易出bug
                searchSourceBuilder.sort(new FieldSortBuilder(sortField).order(sortRule.equals("DESC") ? SortOrder.DESC : SortOrder.ASC));
            }



            //4.构建查询对象
            //NativeSearchQuery query = nativeSearchQueryBuilder.build();
            searchRequest.source(searchSourceBuilder);

            //5.执行查询
            //AggregatedPage<SkuInfo> skuPage = elasticsearchTemplate.queryForPage(query, SkuInfo.class,new SearchResultMapperImpl());
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

            //获取查询命中信息
            SearchHits searchHits = searchResponse.getHits();

            //解析高亮结果  将高亮部分替换原来的数据
            for (SearchHit hit : searchResponse.getHits().getHits()) {
                //获取高亮属性,这里如果看过kibana中的高亮的返回结果的话应该很好理解
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField title = highlightFields.get("name");
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果
                //解析高亮的字段，将原来的字段换位我们高亮的字段即可
                if(title != null){
                    Text[] fragments = title.fragments();
                    String n_title = "";
                    for (Text text : fragments) {
                        n_title += text;
                    }
                    sourceAsMap.put("name", n_title);
                }
            }

            Aggregations aggregations = searchResponse.getAggregations();

            //获取分组结果  商品分类
//        StringTerms stringTermsCategory = (StringTerms) searchResponse.getAggregations().get("skuCategorygroup");
            Terms skuCategorygroup = aggregations.get("skuCategorygroup");
            //获取分组结果  商品品牌
            Terms skuBrandgroup = aggregations.get("skuBrandgroup");
            //获取分组结果  商品规格数据
            Terms skuSpecgroup = aggregations.get("skuSpecgroup");

            List<String> categoryList = getStringsCategoryList(skuCategorygroup);

            List<String> brandList = getStringsBrandList(skuBrandgroup);

            Map<String, Set<Object>> specMap = putAllSpec(skuSpecgroup);

            resultMap = new HashMap<>();

            resultMap.put("specMap", specMap);
            resultMap.put("categoryList", categoryList);
            resultMap.put("brandList", brandList);
            resultMap.put("rows", searchHits.getHits());
            resultMap.put("total", searchHits.getTotalHits());
            resultMap.put("totalPages", searchHits.getTotalHits().value%pageSize == 0 ? searchHits.getTotalHits().value/pageSize : (searchHits.getTotalHits().value/pageSize)+1);
            resultMap.put("pageNum", pageNum);
            resultMap.put("pageSize", pageSize);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放资源(不然时间久了会出现远程强制关闭了一个连接的情况)
            client.close();
        }

        return resultMap;

    }


    /**
     * 获取品牌列表（优化后）
     *
     * @param stringTermsBrand
     * @return
     */
    private List<String> getStringsBrandList(Terms stringTermsBrand) {
        List<String> brandList = new ArrayList<>();
        if (stringTermsBrand != null) {
            for (Terms.Bucket bucket : stringTermsBrand.getBuckets()) {
                brandList.add(bucket.getKeyAsString());
            }
        }
        return brandList;
    }

    /**
     * 获取分类列表数据（优化后）
     *
     * @param stringTerms
     * @return
     */
    private List<String> getStringsCategoryList(Terms stringTerms) {
        List<String> categoryList = new ArrayList<>();
        if (stringTerms != null) {
            for (Terms.Bucket bucket : stringTerms.getBuckets()) {
                String keyAsString = bucket.getKeyAsString();//分组的值
                categoryList.add(keyAsString);
            }
        }
        return categoryList;
    }


    /**
     * 获取规格列表数据(优化后)
     *
     * @param stringTermsSpec
     * @return
     */
    private Map<String, Set<Object>> putAllSpec(Terms stringTermsSpec) {
        Map<String, Set<Object>> specMap = new HashMap<String, Set<Object>>();

        Set<String> specList = new HashSet<>();

        if (stringTermsSpec != null) {
            for (Terms.Bucket bucket : stringTermsSpec.getBuckets()) {
                specList.add(bucket.getKeyAsString());
            }
        }

        for (String specjson : specList) {
            Map<String, String> map = JSON.parseObject(specjson, Map.class);
            for (Map.Entry<String, String> entry : map.entrySet()) {//
                String key = entry.getKey();        //规格名字
                Object value = entry.getValue();    //规格选项值
                Set<Object> specValues = new HashSet<Object>();
                //将当前规格加入到集合中
                specValues.add(value);
                //将数据存入到specMap中
                specMap.put(key, specValues);
            }
        }
        return specMap;
    }

}
